#include "app_device.h"
#include "app_bt.h"
#include "string.h"
#include "stdio.h"
#include "app_common.h"
#include "log.h"
#include "fcntl.h"
#include "unistd.h"
#include "app_serial.h"
#include "termios.h"

/**
 *  @brief  蓝牙模块读取数据
 *  @param  data: 读取到的数据
 *  @param  len: 读取到的数据长度
 *  @retval 0: 成功
 *
 *  问题 ： 从串口中读取的数据有可能不是一个整帧数据
 *  解决： 凑 ， 把数据堆在一个暂存区中，每次d到达新数据是对暂存区进行判断，是否有整帧数据，
 *  有->转换结构发送
 *  没有->等着
 *
 * 一帧数据格式：
 *  f1 dd : 固定的头部
    07： 之后数据的长度（5-16之间）
    23 23：对端（发送方）的MADDR
    ff ff: 我的MADDR或ffff(群发)
    41 42 43：发送的数据
 * 处理后的数据格式：conn_type id_len msg_len id msg
   -> 01 02 03 23 23 41 42 43

    1  先收取数据  写入到暂存区中
    2  遍历暂存区中的数据 匹配帧头 f1 dd
    3  一旦匹配成功   先清理帧头前的数据
    4  判断是否有整帧数据   是否够8个字节（整帧的最小字节数）
    5   取第三个字节 获得后续一帧的长度 2个字节蓝牙下游设备的地址 2个字节网格的地址  n个字节的内容
    6   如果数据长度够   从暂存区中提取数据
    7   拼接转换为一条字节数组
    8  把提取后的字节数组覆盖在参数中
    9  返回字节数组数据长度
 */

char buff[1024];
int buff_data_len = 0;
char read_header[] = {0xf1, 0xdd};

// 清理坐标之前的数据
static void app_bt_clear_buff(int clear_num)
{
    memmove(buff, buff + clear_num, buff_data_len - clear_num);
    buff_data_len -= clear_num;
}
static int app_bt_post_read(char *data, int len)
{
    // 1  先收取数据  写入到暂存区中
    memcpy(buff + buff_data_len, data, len);
    buff_data_len += len;
    // 1.5 打印当前暂存
    printf("当前暂存数据:");
    app_common_print_hex(buff, buff_data_len);

    // 2  遍历暂存区中的数据 匹配帧头 f1 dd
    for (int i = 0; i < buff_data_len; i++)
    {
        if (buff[i] == read_header[0] && buff[i + 1] == read_header[1])
        {
            // 3  一旦匹配成功   先清理帧头前的数据
            app_bt_clear_buff(i);
            // 4  判断是否有整帧数据   是否够8个字节（整帧的最小字节数）
            if (buff_data_len >= 8)
            {
                // 5   取第三个字节 获得后续一帧的长度 2个字节蓝牙下游设备的地址 2个字节网格的地址  n个字节的内容
                int whole_data_len = buff[i + 2] + 3;
                if (buff_data_len >= whole_data_len)
                {
                    // 6   如果数据长度够   从暂存区中提取数据
                    // 7   拼接转换为一条字节数组
                    char temp_data[32];
                    temp_data[0] = BT_CONN_TYPE;
                    temp_data[1] = BT_MADDR_LEN;
                    temp_data[2] = buff[i + 2] - 4; // 数据长度-2个字节蓝牙下游设备的地址-2个字节网格的地址
                    memcpy(temp_data + 3, buff + 3, BT_MADDR_LEN);
                    int msg_len = buff[i + 2] - 4;
                    memcpy(temp_data + 3 + BT_MADDR_LEN, buff + 7, msg_len);
                    // 8  把提取后的字节数组覆盖在参数中
                    int new_data_len = 3 + 2 + msg_len;
                    memcpy(data, temp_data, new_data_len);
                    // 9 把提取的数据从缓冲区中移除
                    app_bt_clear_buff(whole_data_len);
                    // 10  返回字节数组数据长度
                    return new_data_len;
                }
            }
        }
    }

    return 0;
}

/**
 *  @brief  把字节数组转换成蓝牙格式数据
 * 格式：
 * 41 54 2b 4d 45 53 48 00 ff ff 11 22 33 0d 0a
 * 其中
 * 41 54 2b 4d 45 53 48 00  固定帧头
 * ff ff  网络地址
 * 11 22 33 发送的内容
 * 0d 0a  帧尾
 *
 */

static char write_header[] = {0x41, 0x54, 0x2b, 0x4d, 0x45, 0x53, 0x48, 0x00};
static char write_tailer[] = {0x0d, 0x0a};
static int app_bt_pre_write(char *data, int len)
{
    char buff[100];
    memcpy(buff, write_header, 8);               // 帧头
    memcpy(buff + 8, data + 3, len - 3);         // 网络地址+内容 01 02 04 6f 6f 41 42 43 55 -> 6f 6f 41 42 43 55
    memcpy(buff + 8 + len - 3, write_tailer, 2); // 帧尾

    int new_len = 8 + len - 3 + 2;
    memcpy(data, buff, new_len);
    return new_len;
}

/**
 * @brief  为设备进行 蓝牙初始化配置
 * 1 为设备装配 2个预处理的方法 post_read  pre_write
 * 2 为设备配置蓝牙的配置信息  （波特率、网络id、短地址、网络名）
 */
int app_bt_init(app_device_t *device)
{
    //1
    device->post_read = app_bt_post_read;
    device->pre_write = app_bt_pre_write;
    //2
    app_bt_set_default_config(device);
    return 0;
}

/**
 * @brief  测试蓝牙连接
 * @param  device: 设备指针
 * @return 0: 成功， -1: 失败
 */
int app_bt_status(app_device_t *device)
{
    write(device->fd, "AT\r\n", 4);
    int ack = waitAck(device);
    if (ack == 0)
    {
        log_info("app_bt_status: AT OK");
        return 0;
    }
    else
    {
        log_info("app_bt_status: AT FAIL");
        return -1;
    }
}

/**
 * @brief  蓝牙接收返回值
 * @param  device: 设备指针
 * @return 0: 成功， -1: 失败
 */
int waitAck(app_device_t *device)
{
    char ack[10] = {0};
    usleep(100 * 1000);
    ssize_t read_size = read(device->fd, ack, 4);
    printf("read wait_ack size: %d\n", read_size);
    if (read_size > 0 && strcmp(ack, "OK\r\n") == 0)
    {
        return 0;
    }
    return -1;
}

/**
 * @brief  复位蓝牙连接
 * @param  device: 设备指针
 * @return 0: 成功， -1: 失败
 */
int app_bt_reset(app_device_t *device)
{
    write(device->fd, "AT+RESET\r\n", 10);
    return 0;
}

/**
 * @brief  设置广播名
 * @param  device: 设备指针
 * @param  name: 广播名
 * @return 0: 成功， -1: 失败
 */
int app_bt_set_name(app_device_t *device, char *name)
{
    char cmd[50];
    sprintf(cmd, "AT+NAME%s\r\n", name);
    write(device->fd, cmd, strlen(cmd));
    int ack = waitAck(device);
    if (ack == 0)
    {
        log_info("设置广播名成功 ");
        return 0;
    }
    else
    {
        log_info("设置广播名失败 ");
        return -1;
    }
}

/**
 * @brief  设置波特率
 * @param  device: 设备指针
 * @param  baudrate: 波特率
 * @return 0: 成功， -1: 失败
 */
int app_bt_set_baudrate(app_device_t *device, BT_BAUD_RATE baudrate)
{
    char cmd[20];
    sprintf(cmd, "AT+BAUD%c\r\n", baudrate);
    write(device->fd, cmd, strlen(cmd));
    int ack = waitAck(device);
    if (ack == 0)
    {
        log_info("设置波特率成功 ");
        return 0;
    }
    else
    {
        log_info("设置波特率失败 ");
        return -1;
    }
}

/**
 * @brief  设置组网id
 * @param  device: 设备指针
 * @param  netid: 组网id
 * @return 0: 成功， -1: 失败
 */
int app_bt_set_netid(app_device_t *device, char *netid)
{
    char cmd[20];
    sprintf(cmd, "AT+NETID%s\r\n", netid);
    write(device->fd, cmd, strlen(cmd));
    int ack = waitAck(device);
    if (ack == 0)
    {
        log_info("设置netid成功 ");
        return 0;
    }
    else
    {
        log_info("设置netid失败 ");
        return -1;
    }
}

/**
 * @brief  设置组网段地址
 * @param  device: 设备指针
 * @param  netid: 组网段地址
 * @return 0: 成功， -1: 失败
 */
int app_bt_set_maddr(app_device_t *device, char *maddr)
{
    {
        char cmd[20];
        sprintf(cmd, "AT+MADDR%s\r\n", maddr);
        write(device->fd, cmd, strlen(cmd));
        int ack = waitAck(device);
        if (ack == 0)
        {
            log_info("设置maddr成功 ");
            return 0;
        }
        else
        {
            log_info("设置maddr失败 ");
            return -1;
        }
    }
}

/**
 * @brief  设置蓝牙默认e皮质
 * @param  device: 设备指针
 * @return 0: 成功， -1: 失败
 * 1  确保蓝牙和主控联通  2 设置配置
 * 步骤：
 *  0  假设串口 波特率 115200
 *  1  设置串口读取方式为非阻塞模式 flush（避免卡死）
 *  2  测试蓝牙是否联通 status
 *  3  如果不通过 ：
 *      3.1 把串口改为9600  通过flush生效
 *      3.2 再次测试
 *      3.3 如果不通过 （情况 1 测试代码有问题 2 蓝牙芯片有问题 ）
 *      3.4 如果通过  发送AT命令 给蓝牙改为115200波特率
 *      3.5 发送AT复位命令
 *      3.6 等2秒
 *      3.7 把串口改为115200   通过flush生效
 *      3.8 再次测试是否联通 如果通过直接过 没通过改波特率的代码可能有问题。
 *   4 设置其他项： 广播名 网络id  网络短地址
 *   5 设置串口为阻塞模式 flush
 *
 */
int app_bt_set_default_config(app_device_t *device)
{
    // *  0  假设串口 波特率 115200
    // *  1  设置串口读取方式为非阻塞模式 flush（避免卡死）
    app_serial_set_block(device, 0);
    tcflush(device->fd, TCIOFLUSH);

    // *  2  测试蓝牙是否联通 status
    if (app_bt_status(device) == -1)
    {
        log_info("蓝牙测试 未通过,需要重新协调波特率");
        // *  3  如果不通过 ：
        // *      3.1 把串口改为9600  通过flush生效
        
        app_serial_set_baudrate(device, BR_9600);
        tcflush(device->fd, TCIOFLUSH);
        log_info("串口波特率已经设置为9600...");
        // *      3.2 再次测试
        if (app_bt_status(device) == -1)
        {
            // *      3.3 如果不通过 （情况 1 测试代码有问题 2 蓝牙芯片有问题 ）
            log_error("再次测试依然不通过： 1 测试代码有问题 2 蓝牙芯片有问题 ");
            return -1;
        }

        // *      3.4 如果通过  发送AT命令 给蓝牙改为115200波特率
        app_bt_set_baudrate(device, BT_BAUD_115200);
        log_info("蓝牙波特率设置为115200...");
        // *      3.5 发送AT复位命令
        log_info("重启蓝牙模块..");
        app_bt_reset(device);

        // *      3.6 等2秒
        sleep(2);
        // *      3.7 把串口改为115200   通过flush生效
        log_info("串口波特率已经设置为 115200...");
        app_serial_set_baudrate(device, BR_115200);
        tcflush(device->fd, TCIOFLUSH);
        log_info("串口波特率已经设置为 115200...");
        // *      3.8 再次测试是否联通 如果通过直接过 没通过改波特率的代码可能有问题。
        if (app_bt_status(device) == -1)
        {
            log_error(" 修改波特率后最终测试失败");
            return -1;
        }
    }
    // *   4 设置其他项： 广播名 网络id  网络短地址
    app_bt_set_name(device, "atguigu");
    app_bt_set_netid(device, "1234");// 网络id 组号 每组一个
    app_bt_set_maddr(device, "0066");// 组内每人一个 不能跟下游设备重复
    log_info("蓝牙参数设置完成..");
    // *   5 设置串口为阻塞模式 flush
    app_serial_set_block(device, 1);
    tcflush(device->fd, TCIOFLUSH);
 
    return 0;
}